#include "teststdafx.h"
#include "stdafx.h"
#include <gtest/gtest.h>

#include "Business/Policy/ClassicBusinessContext.h"

#include "Business/Business.h"
#include "Business/Command.h"
#include "Business/Market.h"
#include "Business/Dealing.h"
#include "Utils/FastBlockQueue.h"
#include "Repository/DealingRepository.h"
#include "Repository/CancellationRepository.h"
#include "Business/QueueContext.h"
#include "Service/MarketStorage.h"
#include "Business/Customer.h"
#include "Business/RequestTimeout.h"
#include "Business/OrderNotification.h"

using namespace std;
namespace {

class MockBusiness : public Business
{
public:
	MockBusiness(Customer& customer)
		:Business(customer)
	{

	}
	virtual ~MockBusiness() {}
private:
	virtual void DoOnInit()
	{

	}
	virtual void DoOnMarket(Market const& market)
	{
	}
	virtual void DoOnPendingMarket(PendingMarket const& pendingMarket)
	{

	}
	virtual void DoOnDealing(Dealing const& dealing) 
	{
	}
	virtual void DoOnTimeout(RequestTimeout const& requestTimeout)
	{
	}
	virtual void DoOnTrade(Trade const& trade)
	{
	}
	virtual void DoOnCommand(Command const& command)
	{
	}
	virtual void DoOnExchangeStatus(int exchangeStatus)
	{
	}
};

}
class ClassicBusinessContextTest : public testing::Test
{
protected:

	virtual void SetUp()
	{
		HANDLE hQuit = ::CreateEvent(NULL,TRUE,NULL,NULL);
		m_customer = new Customer("","","");
		m_customer->SetOrginalAmount(100000.00);
		m_requestQueue = FastBlockQueue::Create(hQuit);
		m_businessQueue = FastBlockQueue::Create(hQuit);
		m_uiQueue = FastBlockQueue::Create(hQuit);
		m_highTimerQueue = FastBlockQueue::Create(hQuit);
		m_waitTimerQueue = FastBlockQueue::Create(hQuit);
		m_dealingRepository = new DealingRepository();
		m_cancellationRepository = new CancellationRepository();
		m_queueContext = new QueueContext(*m_requestQueue,*m_businessQueue,*m_uiQueue,*m_highTimerQueue,*m_waitTimerQueue);
		m_business = new MockBusiness(*m_customer);
		ArbitrageContracts contracts(0,1);
		Band bearishBand(90,120,60);
		Band bullishBand(120,90,150);
		m_argument = new ClassicBusinessArgument(contracts,bearishBand,bullishBand,10);

		InitMarket();
	}
	virtual void TearDown()
	{

		delete m_argument;
		delete m_business;
		delete m_queueContext;
		delete m_cancellationRepository;
		delete m_dealingRepository;
		delete m_highTimerQueue;
		delete m_waitTimerQueue;
		delete m_uiQueue;
		delete m_businessQueue;
		delete m_requestQueue;
		delete m_customer;
	}
	void InitMarket()
	{
		InitMarket0();
		InitMarket1();
	}
	void InitMarket0()
	{
		double lastPrice = 3000.00;
		Prices buyPrices;
		buyPrices[0] = Price(3010,7);
		Prices sellPrices;
		sellPrices[0] = Price(2990,6);
		double upperLimit = 4000.00;
		double lowerLimit = 2000.00;
		double preSettlementPrice  = 3000.00;
		m_marketStorage.Save(Market(0,CreateVariety("rb1010"),lastPrice,0,buyPrices,sellPrices,upperLimit,lowerLimit,preSettlementPrice)); 
	}
	void InitMarket1()
	{
		double lastPrice = 3100.00;
		Prices buyPrices;
		buyPrices[0] = Price(3000,9);
		Prices sellPrices;
		sellPrices[0] = Price(2980,8);
		double upperLimit = 4000.00;
		double lowerLimit = 2000.00;
		double preSettlementPrice  = 3100.00;
		m_marketStorage.Save(Market(1,CreateVariety("rb1011"),lastPrice,0,buyPrices,sellPrices,upperLimit,lowerLimit,preSettlementPrice)); 
	}

	Variety CreateVariety(char const* code)
	{
		Variety variety(code,1,10);
		Margins margins;
		margins.Add(Margin("test1",0.1));
		margins.Add(Margin("test2",0.2));
		variety.SetMargins(margins);
		Charge openExchageCharge("open exchange charge",Charge::PERCENT, 0.10);
		Charge openCompanyCharge("open company charge",Charge::PER_QUANTITY, 5);
		Charge offsetExchangeCharge("offset exchange charge",Charge::PER_QUANTITY, 10);
		Charge offsetCompanyCharge("offset company charge",Charge::PERCENT, 0.10);
		Charges openCharges;
		openCharges.push_back(openExchageCharge);
		openCharges.push_back(openCompanyCharge);
		Charges offsetCharges;
		offsetCharges.push_back(offsetExchangeCharge);
		offsetCharges.push_back(offsetCompanyCharge);
		variety.SetOperationCharges(Operation::OPEN, openCharges);
		variety.SetOperationCharges(Operation::OFFSET,offsetCharges);
		return variety;
	}
	void UpdateBearishCanOpenMarket()
	{
		Variety variety("UNDEFINED",-1,-1);
		// update market 0
		{
			double lastPrice = 3000.00;
			Prices buyPrices;
			buyPrices[0] = Price(3000,10);
			Prices sellPrices;
			sellPrices[0] = Price(2980,5);
			double upperLimit = 4000.00;
			double lowerLimit = 2000.00;
			double preSettlementPrice  = 3100.00;
			m_marketStorage.UpdatePrice(Market(0,variety,lastPrice,0,buyPrices,sellPrices,upperLimit,lowerLimit,preSettlementPrice)); 
		}
		// update market 1
		{
			double lastPrice = 3000.00;
			Prices buyPrices;
			buyPrices[0] = Price(3050,10);
			Prices sellPrices;
			sellPrices[0] = Price(3090,5);
			double upperLimit = 4000.00;
			double lowerLimit = 2000.00;
			double preSettlementPrice  = 3100.00;
			m_marketStorage.UpdatePrice(Market(1,variety,lastPrice,0,buyPrices,sellPrices,upperLimit,lowerLimit,preSettlementPrice)); 
		}
	}

	void UpdateBullishCanOpenMarket()
	{
		Variety variety("UNDEFINED",-1,-1);
		// update market 0
		{
			double lastPrice = 3000.00;
			Prices buyPrices;
			buyPrices[0] = Price(3000,10);
			Prices sellPrices;
			sellPrices[0] = Price(2980,5);
			double upperLimit = 4000.00;
			double lowerLimit = 2000.00;
			double preSettlementPrice  = 3100.00;
			m_marketStorage.UpdatePrice(Market(0,variety,lastPrice,0,buyPrices,sellPrices,upperLimit,lowerLimit,preSettlementPrice)); 
		}
		// update market 1
		{
			double lastPrice = 3000.00;
			Prices buyPrices;
			buyPrices[0] = Price(3100,10);
			Prices sellPrices;
			sellPrices[0] = Price(3130,5);
			double upperLimit = 4000.00;
			double lowerLimit = 2000.00;
			double preSettlementPrice  = 3100.00;
			m_marketStorage.UpdatePrice(Market(1,variety,lastPrice,0,buyPrices,sellPrices,upperLimit,lowerLimit,preSettlementPrice)); 
		}
	}

	void UpdateMinorCloseUpperLimit()
	{
		Variety variety("UNDEFINED",-1,-1);
		double lastPrice = 3990.00;
		Prices buyPrices;
		buyPrices[0] = Price(3050,10);
		Prices sellPrices;
		sellPrices[0] = Price(3090,5);
		double upperLimit = 4000.00;
		double lowerLimit = 2000.00;
		double preSettlementPrice  = 3100.00;
		m_marketStorage.UpdatePrice(Market(1,variety,lastPrice,0,buyPrices,sellPrices,upperLimit,lowerLimit,preSettlementPrice)); 
	}
	void UpdateMinorCloseLowerLimit()
	{
		Variety variety("UNDEFINED",-1,-1);
		double lastPrice = 2010.00;
		Prices buyPrices;
		buyPrices[0] = Price(3050,10);
		Prices sellPrices;
		sellPrices[0] = Price(3090,5);
		double upperLimit = 4000.00;
		double lowerLimit = 2000.00;
		double preSettlementPrice  = 3100.00;
		m_marketStorage.UpdatePrice(Market(1,variety,lastPrice,0,buyPrices,sellPrices,upperLimit,lowerLimit,preSettlementPrice)); 
	}
	void UpdateMajorCloseUpperLimit()
	{
		Variety variety("UNDEFINED",-1,-1);
		double lastPrice = 3990.00;
		Prices buyPrices;
		buyPrices[0] = Price(3050,10);
		Prices sellPrices;
		sellPrices[0] = Price(3090,5);
		double upperLimit = 4000.00;
		double lowerLimit = 2000.00;
		double preSettlementPrice  = 3100.00;
		m_marketStorage.UpdatePrice(Market(0,variety,lastPrice,0,buyPrices,sellPrices,upperLimit,lowerLimit,preSettlementPrice)); 
	}
	void UpdateMajorCloseLowerLimit()
	{
		Variety variety("UNDEFINED",-1,-1);
		double lastPrice = 2010;
		Prices buyPrices;
		buyPrices[0] = Price(3050,10);
		Prices sellPrices;
		sellPrices[0] = Price(3090,5);
		double upperLimit = 4000.00;
		double lowerLimit = 2000.00;
		double preSettlementPrice  = 3100.00;
		m_marketStorage.UpdatePrice(Market(0,variety,lastPrice,0,buyPrices,sellPrices,upperLimit,lowerLimit,preSettlementPrice)); 
	}

	MarketStorage m_marketStorage;
	FastBlockQueue* m_requestQueue;
	FastBlockQueue* m_businessQueue;
	FastBlockQueue* m_uiQueue;
	FastBlockQueue* m_highTimerQueue;
	FastBlockQueue* m_waitTimerQueue;
	Customer* m_customer;
	MockBusiness* m_business;
	DealingRepository* m_dealingRepository;
	CancellationRepository* m_cancellationRepository;
	QueueContext* m_queueContext;
	ClassicBusinessArgument* m_argument;

	void foo();
};


ostream& operator<<(ostream& os, Band const& band)
{
	os << band.GetOpen() << '|'<< band.GetOffset() << '|'<< band.GetStop();
	return os;
}

TEST_F(ClassicBusinessContextTest, ClassicArbitragePositionCtorTest) /*
// void ClassicBusinessContextTest::foo() /* */
{
	{
		ArbitrageContracts contracts(0,1);
		ClassicBusinessPosition position(contracts,PositionValue(1000,9),PositionValue(1000,1));

		EXPECT_EQ(9, position.GetMajor().GetQuantity());
		EXPECT_EQ(1, position.GetMinor().GetQuantity());
		EXPECT_EQ(9, position.GetNearby().GetQuantity());
		EXPECT_EQ(1, position.GetFarther().GetQuantity());
	}
	{
		ArbitrageContracts contracts(1,0);
		ClassicBusinessPosition position(contracts,PositionValue(1000,9),PositionValue(1000,1));

		EXPECT_EQ(9, position.GetMajor().GetQuantity());
		EXPECT_EQ(1, position.GetMinor().GetQuantity());
		EXPECT_EQ(1, position.GetNearby().GetQuantity());
		EXPECT_EQ(9, position.GetFarther().GetQuantity());
	}

}

TEST_F(ClassicBusinessContextTest, ClassicArbitragePositionIsZeroTest) /*
// void ClassicBusinessContextTest::foo() /**/
{
	{
		ArbitrageContracts contracts(0,1);
		ClassicBusinessPosition position(contracts,PositionValue(1000,9),PositionValue(1000,1));

		EXPECT_EQ(false, position.IsZero());
	}
	{
		ArbitrageContracts contracts(1,0);
		ClassicBusinessPosition position(contracts);

		EXPECT_EQ(true, position.IsZero());
	}

	{
		ArbitrageContracts contracts(1,0);
		ClassicBusinessPosition position(contracts,PositionValue(1000,0),PositionValue(1000,1));

		EXPECT_EQ(false, position.IsZero());
	}
}

TEST_F(ClassicBusinessContextTest, ClassicArbitragePositionUpdateTest) /*
void ClassicBusinessContextTest::foo() /**/
{
	int major = 0;
	int minor = 1;
	ArbitrageContracts contracts(major,minor);
	ClassicBusinessPosition position(contracts);
	EXPECT_EQ(true,position.IsZero());

	position.Update(major,Operation::OPEN, PositionValue(1000,10));
	EXPECT_EQ(false,position.IsZero());
	EXPECT_EQ(10,position.GetMajor().GetQuantity());
	EXPECT_EQ(10,position.GetNearby().GetQuantity());
	EXPECT_EQ(0,position.GetMinor().GetQuantity());
	EXPECT_EQ(0,position.GetFarther().GetQuantity());

	position.Update(minor,Operation::OPEN,PositionValue(1000,5));
	EXPECT_EQ(false,position.IsZero());
	EXPECT_EQ(10,position.GetMajor().GetQuantity());
	EXPECT_EQ(10,position.GetNearby().GetQuantity());
	EXPECT_EQ(5,position.GetMinor().GetQuantity());
	EXPECT_EQ(5,position.GetFarther().GetQuantity());

	position.Update(major,Operation::OFFSET_TODAY,PositionValue(1000,3));
	EXPECT_EQ(false,position.IsZero());
	EXPECT_EQ(7,position.GetMajor().GetQuantity());
	EXPECT_EQ(7,position.GetNearby().GetQuantity());
	EXPECT_EQ(5,position.GetMinor().GetQuantity());
	EXPECT_EQ(5,position.GetFarther().GetQuantity());

	position.Update(major,Operation::OFFSET_TODAY,PositionValue(1000,7));
	EXPECT_EQ(false,position.IsZero());
	EXPECT_EQ(0,position.GetMajor().GetQuantity());
	EXPECT_EQ(0,position.GetNearby().GetQuantity());
	EXPECT_EQ(5,position.GetMinor().GetQuantity());
	EXPECT_EQ(5,position.GetFarther().GetQuantity());

	position.Update(minor,Operation::OFFSET_TODAY,PositionValue(1000,5));
	EXPECT_EQ(true,position.IsZero());
	EXPECT_EQ(0,position.GetMajor().GetQuantity());
	EXPECT_EQ(0,position.GetNearby().GetQuantity());
	EXPECT_EQ(0,position.GetMinor().GetQuantity());
	EXPECT_EQ(0,position.GetFarther().GetQuantity());

}


TEST_F(ClassicBusinessContextTest, CtorTest) /*
void ClassicBusinessContextTest::foo() /**/
{
	ClassicBusinessContext context(*m_customer,*m_business,*m_argument,m_marketStorage,*m_queueContext,*m_dealingRepository,*m_cancellationRepository);

	EXPECT_EQ(0, context.GetMajor());
	EXPECT_EQ(1, context.GetMinor());
	EXPECT_EQ(m_business,context.GetBusiness());
}

TEST_F(ClassicBusinessContextTest, BearishTryOpenTest) /*
void ClassicBusinessContextTest::foo() /**/
{
	ClassicBusinessContext context(*m_customer,*m_business,*m_argument,m_marketStorage,*m_queueContext,*m_dealingRepository,*m_cancellationRepository);

	EXPECT_EQ(false,context.TryOpen());

	UpdateBearishCanOpenMarket();

	EXPECT_EQ(true,context.TryOpen());
	EXPECT_EQ(4,context.GetMinorOpenQuantity());
	EXPECT_EQ(m_argument->GetBearish(),context.GetCurrrentLegs().GetBand());
}

TEST_F(ClassicBusinessContextTest, BullishTryOpenTest) /*
void ClassicBusinessContextTest::foo() /**/
{
	ClassicBusinessContext context(*m_customer,*m_business,*m_argument,m_marketStorage,*m_queueContext,*m_dealingRepository,*m_cancellationRepository);

	EXPECT_EQ(false,context.TryOpen());

	UpdateBullishCanOpenMarket();

	EXPECT_EQ(true,context.TryOpen());
	EXPECT_EQ(4,context.GetMinorOpenQuantity());
	EXPECT_EQ(m_argument->GetBullish(),context.GetCurrrentLegs().GetBand());
}

TEST_F(ClassicBusinessContextTest, NotEnoughFundsTest) /*
void ClassicBusinessContextTest::foo() /**/
{
	m_customer->SetOrginalAmount(10);
	ClassicBusinessContext context(*m_customer,*m_business,*m_argument,m_marketStorage,*m_queueContext,*m_dealingRepository,*m_cancellationRepository);

	EXPECT_EQ(false,context.TryOpen());

	UpdateBearishCanOpenMarket();

	EXPECT_EQ(false,context.TryOpen());
	EXPECT_EQ(0,context.GetMinorOpenQuantity());
}

ostream& operator<<(ostream& os, Dealing const& dealing)
{
	UNREFERENCED_PARAMETER(dealing);
	return os;
}

bool operator==(Dealing const& l, Dealing const& r)
{
	return 	l.GetContract() == r.GetContract() &&
			&l.GetCustomer() == &r.GetCustomer() &&
 			l.GetBusiness() == r.GetBusiness() &&
  			l.GetOperation() == r.GetOperation() &&
 			l.GetDirection() == r.GetDirection() &&
 			l.GetOrderQuantity() == r.GetOrderQuantity() &&
 			l.GetOrderPrice() == r.GetOrderPrice();
}

TEST_F(ClassicBusinessContextTest, OpenMinorTest) /*
void ClassicBusinessContextTest::foo() /**/
{
	ClassicBusinessContext context(*m_customer,*m_business,*m_argument,m_marketStorage,*m_queueContext,*m_dealingRepository,*m_cancellationRepository);
	bool quitFlag = false;

	EXPECT_EQ(false,context.TryOpen());
	UpdateBearishCanOpenMarket();

	context.TryOpen();
	context.OpenMinor();

	EXPECT_EQ(1,m_queueContext->GetRequestQueue().Size());

	QueueItem& item = m_queueContext->GetRequestQueue().Pop(quitFlag);
	Dealing& actualDealing = *(Dealing*)item.data;

	Order order;
	order.businessId = m_business->GetId();
	order.businessPointer = m_business;
	order.contract = m_argument->GetContracts().GetMinor();
	order.direction = Direction::BUY;
	order.operation = Operation::OPEN;
	order.quantity = 4;
	order.price = m_marketStorage.GetMarket(order.contract).GetSellPrices()[0].GetPrice();
	Dealing expectedDealing(*m_customer,order);

	EXPECT_EQ(expectedDealing, actualDealing);
}

ostream& operator<<(ostream& os, Cancellation const& cancellation)
{
	UNREFERENCED_PARAMETER(cancellation);
	return os;
}

bool operator==(Cancellation const& l, Cancellation const& r)
{
	return 	l.GetContract() == r.GetContract() &&
		l.GetBusiness() == r.GetBusiness() &&
		l.GetOrderLocalId() == r.GetOrderLocalId() &&
		strcmp(l.GetSystemNo(),r.GetSystemNo()) == 0;
}

TEST_F(ClassicBusinessContextTest, CancelMinorOpenTest) /*
void ClassicBusinessContextTest::foo() /**/
{
	ClassicBusinessContext context(*m_customer,*m_business,*m_argument,m_marketStorage,*m_queueContext,*m_dealingRepository,*m_cancellationRepository);
	bool quitFlag = false;

	EXPECT_EQ(false,context.TryOpen());
	UpdateBearishCanOpenMarket();

	context.TryOpen();
	context.OpenMinor();

	EXPECT_EQ(1,m_queueContext->GetRequestQueue().Size());

	QueueItem* item = &m_queueContext->GetRequestQueue().Pop(quitFlag);
	Dealing& dealing = *(Dealing*)item->data;
	Dealing* pDealing = m_dealingRepository->Get(dealing.GetLocalId());
	pDealing->OnOrdered("hahaha");

	context.CancelMinorOpen();
	EXPECT_EQ(1,m_queueContext->GetRequestQueue().Size());


	Cancellation expectedCancellation(m_business,m_business->GetId(),m_argument->GetMinor(),1,pDealing->GetLocalId(),pDealing->GetSystemNo());
	item = &m_queueContext->GetRequestQueue().Pop(quitFlag);
	Cancellation& actualCancellation = *(Cancellation*)item->data;
	EXPECT_EQ(expectedCancellation,actualCancellation);
}

TEST_F(ClassicBusinessContextTest, IsMarketCloseLimitTest) /*
void ClassicBusinessContextTest::foo() /**/
{
	ClassicBusinessContext context(*m_customer,*m_business,*m_argument,m_marketStorage,*m_queueContext,*m_dealingRepository,*m_cancellationRepository);
	InitMarket();
	EXPECT_EQ(false,context.IsMarketCloseLimit());

	UpdateMinorCloseUpperLimit();
	EXPECT_EQ(true,context.IsMarketCloseLimit());

	InitMarket();
	UpdateMinorCloseLowerLimit();
	EXPECT_EQ(true,context.IsMarketCloseLimit());

	InitMarket();
	UpdateMajorCloseUpperLimit();
	EXPECT_EQ(true,context.IsMarketCloseLimit());

	InitMarket();
	UpdateMajorCloseLowerLimit();
	EXPECT_EQ(true,context.IsMarketCloseLimit());
}
